Aujourd’hui je voudrai partager avec vous quelques petites choses que j’ai appris concernant plotly, une puissante librairie de génération de graphique sous python.
Un peu de contexte :
A la fin de la dernière session de __[data for good](https://dataforgood.fr/)__, je me suis rapprochée de l’équipe de __[Codecarbon]( https://codecarbon.io/)__, un projet enthousiasmant porté par __[Benoit](https://github.com/benoit-cty)__ et __[Victor](https://github.com/vict0rsch)__ (entre autre) dont l’objectif est de calculer l’impact carbone des algorithmes utilisés en data science (si le projet vous interesse n’hésitez pas à venir faire un tour sur le __[slack](data-for-good.slack.com)__).
L’idée de codecarbon est notamment de proposer un Dashboard à ses utilisateurs et le choix de l’outils c’est porté sur __[plotly dash](https://dash.plotly.com/)__ du coup je me suis beaucoup intéressée à __[Plotly](https://plotly.com/python/)__.
Je vais essayer de vous montrer comment, pas à pas, on construit un bubble chart avec cet outil.
#les librairies
#*******************************************************************************
import plotly.graph_objects as go
import pandas as pd
from datetime import date
Première chose à savoir c'est qu'il existe 2 façons de faire des graphs avec plotly:
* plotly express qui est la méthode la plus récente et la plus simple <br>
* les graph object qui est la méthode traditionnelle et plus customisable <br><br>
Comme je suis vielle, je travaille en graph object ;) </font>
# data
# *******************************************************************************
df = pd.read_csv('emissions.csv')
df.timestamp = pd.to_datetime(df.timestamp)
df = df[['timestamp', 'project_name', 'duration',
'emissions', 'energy_consumed', 'country_name']]
df = df.groupby("project_name").agg({"timestamp": 'min', "duration": 'sum',
'emissions': 'sum', 'energy_consumed': 'sum', 'country_name': 'min'}).reset_index()
df
| project_name | timestamp | duration | emissions | energy_consumed | country_name | |
|---|---|---|---|---|---|---|
| 0 | project_alpha | 2020-03-25 21:26:11 | 695 | 118 | 104 | Canada |
| 1 | project_beta | 2020-04-04 21:26:11 | 383 | 76 | 75 | Canada |
| 2 | project_delta | 2020-03-24 15:26:11 | 634 | 112 | 97 | Taiwan |
| 3 | project_gamma | 2020-03-23 09:26:11 | 792 | 120 | 116 | USA |
| 4 | project_theta | 2020-03-27 03:26:11 | 551 | 99 | 95 | Brazil |
Les données utilisées ici sont disponibles sur le __[github de codeCarbon](https://github.com/mlco2/codecarbon)__ .
Rentrons dans le vif du sujet et construisons notre figure de base
fig= go.Figure(
go.Scatter( x=df.timestamp, y=df.emissions,mode='markers',
)
)
fig.show()
Pour l'instant rien de très folichon.
Nous avons construit un graphique qui lie des projets à un niveau d'emissione en kg eq. CO2.
J'ai utilisé les 'mode' markers pour pouvoir affecter une taille à mes bulles en fonction de la durée de mes projets en appelant l'argument 'marker_size'
Ici les durées sont exprimées dans des valeurs bien plus grandes que mes emissions donc je vais devoir retravailler un peu mes "bulles" pour qu'elles soient harmonieuses dans mon graphique.
Pour cela je vais utiliser l'argument marker_sizeref et la formule suivante :
$ \frac{2.*max(array)}{(taille-max-souhaitée) ²}$
j'impose aussi une taille minimum avec l'argument marker_sizemin
</font>
fig= go.Figure(
go.Scatter( x=df.timestamp, y=df.emissions,mode='markers',
marker_size=df.duration, marker_sizeref=2.*max(df.duration)/(15**2), marker_sizemin=10, #15 est choisi pour avoir un rendu à mon goût
)
)
fig.show()
Ca ressemble déja plus à quelque chose.
Maintenant ajoutons de la couleur
Je vais colorer mes bulles en fonction de l'énergie consommées. Je vais faire ça avec 2 arguments marker_color et marker_showscale puisqu'ici je travaille avec des valeurs continues.
</font>
fig = go.Figure(
go.Scatter(x=df.timestamp, y=df.emissions, mode='markers',
#taille des markers
marker_size=df.duration, marker_sizeref=2.*max(df.duration)/(15**2), marker_sizemin=10, #marker_line=dict(width=2, color='red')
#couleur des markers
marker_color=df.energy_consumed, marker_showscale=True
)
)
fig.show()
L'un des énormes avantages de plotly ce sont les infobulles.
A chaque point du graphique on peut associer des informations. Par défaut vont apparaitre les valeurs coordonnées du point (ici la date de début du projet et les émissions de CO2)
On peut évidement customiser tout ça.
Ca se passe dans le hovertemplate qui va nous permettre d'appeler les différentes variable de notre graph sous la forme %{var} et d'utiliser des mises en forme type html. On note que dans les variables au nom composé le "_" devient un "."
les possibilités des hover sont nombreuses n'hésitez pas à vous référer à la __[documentation](https://plotly.com/python/hover-text-and-formatting/)__ .
Nativement quand on travailler avec hovertemplate, un colonne apparait à droite de l'infobulle si on ne souhaite pas qu'elle apparaisse, il faut inscrire un extra vide qui va indiquer à plotly que l'on souhaite que cette partie reste vide.
fig= go.Figure(
go.Scatter(x=df.timestamp, y=df.emissions, mode='markers',
#taille des markers
marker_size=df.duration, marker_sizeref=2.*max(df.duration)/(15**2), marker_sizemin=10, #marker_line=dict(width=2, color='red')
#couleur des markers
marker_color=df.energy_consumed, marker_showscale=True,
#hover
text= df.project_name ,
hovertemplate="<b>%{text}</b><br><br>"+"Emissions:%{y} kg eq. CO2<br>"+
"Energy consumed :%{marker.color} kWh <br>"+ "Duration: %{marker.size} min<extra></extra>"#<extra></extra> is used to remove the side box of the hover
))
fig.show()
On pourrait en rester là mais on va faire un peu de déco.
Voici le logo de code carbon:
Je vais m'en inspirer.
# colors
#****************************************************************
darkgreen = '#024758'
vividgreen = '#c9fb37'
color3 = '#226a7a'
titleColor = '#d8d8d8'
On va donc entrer dans une deuxième partie du code de mon graphique update_layout
Commençons par ajouter un titre et définir sa police, sa couleur...
Nous pouvons choisir les fonds ici j'ai choisi 2 couleurs : "darkgreen" et rose pour que vous puissiez facilement faire la difference entre le paper_bgcolor et le plot_bgcolor.
Comme je vais choisir une couleur de fond foncée je vais passer mes écritures en blanc avec l'argument font_color
</font>
fig= go.Figure(
go.Scatter(x=df.timestamp, y=df.emissions, mode='markers',
#taille des markers
marker_size=df.duration, marker_sizeref=2.*max(df.duration)/(15**2), marker_sizemin=10, #marker_line=dict(width=2, color='red')
#couleur des markers
marker_color=df.energy_consumed, marker_showscale=True,
#hover
text= df.project_name ,
hovertemplate="<b>%{text}</b><br><br>"+"emissions:%{y} kg eq. CO2<br>"+
"Energy consumed :%{marker.color} kWh <br>"+ "duration: %{marker.size} min<extra></extra>", #<extra></extra> is used to remove the side box of the hover
))
fig.update_layout(
#titre
title_text= "PROJECT ",
title_font_color= titleColor,
title_font_family="verdana",
title_font_size=30,
#corps
font_color='white',
paper_bgcolor=darkgreen,
plot_bgcolor='pink',
)
fig.show()
Je ne vais évidemment pas garder ce fond rose.
On va maintenant s'occuper des axes avec update_xaxes et update_yaxes
Dans le cas présent pour aérer le graphique, je vais supprimer la grille en passant l'argument showgrid en False.
Je ne vais garder qu'une ligne pour faire le socle du graphique avec les argument showline, linewidth, et linecolor
Pour rendre la compréhension du graphique plus immédiate j'ajouter un titre à l'axe des y.
</font>
fig= go.Figure(
go.Scatter(x=df.timestamp, y=df.emissions, mode='markers',
#taille des markers
marker_size=df.duration, marker_sizeref=2.*max(df.duration)/(15**2), marker_sizemin=10, #marker_line=dict(width=2, color='red')
#couleur des markers
marker_color=df.energy_consumed, marker_showscale=True,
#hover
text= df.project_name ,
hovertemplate="<b>%{text}</b><br><br>"+"emissions:%{y} kg eq. CO2<br>"+
"Energy consumed :%{marker.color} kWh <br>"+ "duration: %{marker.size} min<extra></extra>"#<extra></extra> is used to remove the side box of the hover
))
fig.update_layout(
title_text= "PROJECT ",
title_font_color= titleColor,
font= dict(color='white'),
paper_bgcolor=darkgreen,
plot_bgcolor=darkgreen,
)
fig.update_xaxes(showgrid=False, showline=True, linewidth=2, linecolor='white',)
fig.update_yaxes(showgrid=False, title="emissions")
fig.show()
On y est presque !
On va maintenant retravailler les " bulles".
On va d'abord ajouter un titre à notre color bar avec marker_colorbar_title et, comme il est un peu long, on va le positionner sur le côté avec marker_colorbar_title_side='right' (on a la choix entre right, top, bottom ).
On va changer la palette de la colorbar avec marker_colorscale.Comme vous pouvez le voir il suffit de lui donner une liste de couleur (ici 2 mais j'aurais pu lui en donner une 3e pour créer une palette divergente). J'ajoute un contour avec marker_line_color et je défini la largeur du contour avec marker_line_width
</font>
fig= go.Figure(
go.Scatter(x=df.timestamp, y=df.emissions, mode='markers',
#taille des markers
marker_size=df.duration, marker_sizeref=2.*max(df.duration)/(15**2), marker_sizemin=10, #marker_line=dict(width=2, color='red')
#couleur des markers
marker_color=df.energy_consumed, marker_showscale=True,
marker_colorbar_title="Energy consumed",marker_colorbar_title_side='right',
marker_colorscale=[darkgreen,vividgreen],marker_line_color=vividgreen, marker_line_width= 4,
#hover
text= df.project_name ,
hovertemplate="<b>%{text}</b><br><br>"+"emissions:%{y} kg eq. CO2<br>"+
"Energy consumed :%{marker.color} kWh <br>"+ "duration: %{marker.size} min<extra></extra>"#<extra></extra> is used to remove the side box of the hover
))
fig.update_layout(
title_text= "Experiment Alpha ",
title_font_color= titleColor,
font= dict(color='white'),
paper_bgcolor=darkgreen,
plot_bgcolor=darkgreen,
)
fig.update_xaxes(showgrid=False, showline=True, linewidth=2, linecolor='white',)
fig.update_yaxes(showgrid=False, visible=True, title="emissions")
fig.show()
Si vous êtes parvenus jusqu'ici BRAVO et merci.
J'espère que ce petit guide pas à pas vous a été utile et qu'il vous a donner des idées pour personnaliser vos graphiques.
Data for good en général et code Carbon en particulier sont toujours à la recherche de bénévoles pour les aider sur leur projet alors si vous êtes intéressés n'hésitez pas à vous présenter sur le slack !
Encore merci d'avoir pris le temps de me lire. Prenez soin de vous! </font>